#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/mod_devicetable.h>
#include "stm32mp157_rcc.h"
#include "stm32mp157_gpio.h"
#include "led_plat.h"

struct stm32mp157_led{
	unsigned int major;
	struct class * clazz;
	struct device * dev;
	rcc_t * rcc;
	gpio_t * gpio;
	struct led_plat_data * pdata;
};

struct stm32mp157_led **led_arr;

ssize_t led_drv_write(struct file * filp, const char __user * buff, size_t size, loff_t * pos)
{
	int ret;
	int led_cmd;
	int minor;
	int index;
	struct stm32mp157_led * led;
	struct inode * node;
	node = filp->f_path.dentry->d_inode;
	minor = iminor(node);
	index = minor - 1;
	led = led_arr[index];
	
	//把用户空间的数据拷贝到内核空间(内核空间不能直接访问用户空间)
	//返回值>0: 没有拷贝成功的个数，失败！
	ret = copy_from_user(&led_cmd, buff, size);
	if(ret > 0)
	{
		printk("copy_from_user error\n");
		return -EAGAIN;
	}
	if(led_cmd == 0)
		led->gpio->ODR &= led->pdata->GPIO_ODR_RESET;
	else
		led->gpio->ODR |= led->pdata->GPIO_ODR_SET;
	return 0;
}
	
int led_drv_open(struct inode * node, struct file * filp)
{
	printk("----------%s----------\n",__FUNCTION__);
	return 0;
}
	
int led_drv_release(struct inode * node, struct file * filp)
{
	printk("----------%s----------\n",__FUNCTION__);
	return 0;
}

const struct file_operations led_fops = {
	.write = led_drv_write,
	.open = led_drv_open,
	.release = led_drv_release,
};

int led_drv_probe(struct platform_device * pdev)
{
	struct stm32mp157_led * led;
	int index;
	struct resource * rcc;
	struct resource * gpio;
	int ret;

	rcc = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	gpio = platform_get_resource(pdev, IORESOURCE_MEM, 1);

	//0.创建全局的结构体对象
	led = kzalloc(sizeof(struct stm32mp157_led), GFP_KERNEL);
	if(!led)
	{
		printk("kzalloc error\n");
		return -ENOMEM;
	}

	led->pdata = (struct led_plat_data *)pdev->dev.platform_data;
	
	//1.注册/申请设备号(高12=主设备号，哪一类设备，内核中唯一；低20=次设备号，该类设备中的哪一个，自己指定)
	//参数1：主设备号(如果传0，让内核自动分配一个未使用的设备号)
	//参数2：设备的描述，自己定义
	//参数3：设备操作函数集
	led->major = register_chrdev(0, led->pdata->led_name, &led_fops);
	if(led->major < 0)
	{
		ret = led->major;
		printk("register_chrdev error\n");
		goto kzalloc_err;
	}

	//2.创建类
	led->clazz = class_create(THIS_MODULE, led->pdata->led_name);
	if(IS_ERR(led->clazz))
	{
		ret = PTR_ERR(led->clazz);
		printk("class_create error\n");
		goto register_chrdev_err;
	}

    //3.创建设备节点
	//手动创建设备节点
	//mknod /dev/led_drv c 240 0
	//参数1: 类的指针
	//参数2: 父类
	//参数3: 设备号
	//参数4: 私有数据
	//参数5: 设备节点的名称
	led->dev = device_create(led->clazz, NULL, MKDEV(led->major, led->pdata->minor), NULL, led->pdata->dev_name);
	if(IS_ERR(led->dev))
	{
		ret = PTR_ERR(led->dev);
		printk("device_create error\n");
		goto class_create_err;
	}

	//4.硬件初始化
	//虚拟地址映射
	led->rcc = ioremap(rcc->start, resource_size(rcc));
	if(!led->rcc)
	{
		ret = -ENOMEM;
		printk("ioremap_rcc error\n");
		goto device_create_err;
	}
	//开启锁相环
	led->rcc->PLL4CR |= led->pdata->RCC_PLL4CR_SET;
	//判断PLL时钟是否稳定
	while(!(led->rcc->PLL4CR & led->pdata->RCC_PLL4CR_TEST))
	{
	}
	//开启GPIOE和GPIOF时钟
	led->rcc->MP_AHB4ENSETR |= led->pdata->RCC_MP_AHB4ENSETR_SET;

    //设置GPIOZ
	led->gpio = ioremap(gpio->start, resource_size(gpio));
	if(!led->gpio)
	{
		ret = -ENOMEM;
		printk("ioremap_gpioz error\n");
		goto ioremap_rcc_err;
	}
	led->gpio->MODER &= led->pdata->GPIO_MODER_RESET;
	led->gpio->MODER |= led->pdata->GPIO_MODER_SET;
	led->gpio->OTYPER &= led->pdata->GPIO_OTYPER_RESET;
	led->gpio->OSPEEDR &= led->pdata->GPIO_OSPEEDR_RESET;
	led->gpio->ODR &= led->pdata->GPIO_ODR_RESET;

	index = led->pdata->minor - 1;
	led_arr[index] = led;
	return 0;

	ioremap_rcc_err:
		iounmap(led->rcc);
	device_create_err:
		device_destroy(led->clazz, MKDEV(led->major, led->pdata->minor));
   	class_create_err:
		class_destroy(led->clazz);
	register_chrdev_err:
		unregister_chrdev(led->major, led->pdata->led_name);
	kzalloc_err:
		kfree(led);
	return ret;
}

int led_drv_remove(struct platform_device * pdev)
{
	struct stm32mp157_led * led;
	int index;
	struct led_plat_data * pdata;
	pdata = (struct led_plat_data *)pdev->dev.platform_data;
	index = pdata->minor - 1;
	led = led_arr[index];
	iounmap(led->gpio);
	iounmap(led->rcc);
	device_destroy(led->clazz, MKDEV(led->major, pdata->minor));
	class_destroy(led->clazz);
	unregister_chrdev(led->major, pdata->led_name);
	kfree(led);
	return 0;
}

const struct platform_device_id led_table[] = {
	{ "stm32mp157_led1", 0x1234 },
	{ "stm32mp157_led2", 0x2234 },
	{ "stm32mp157_led3", 0x3234 },
	{ "stm32mp157_led4", 0x4234 },
	{ "stm32mp157_led5", 0x5234 },
	{ "stm32mp157_led6", 0x6234 },
	{ }
};

struct platform_driver led_drv = {
	.probe = led_drv_probe,
	.remove = led_drv_remove,
	.driver = {
	    //1. 作为备胎和platform_device匹配
	    //2. 在/sys/bus/platform/driver/下创建对应的驱动目录
		.name = "stm32mp157_led_drv",
	},
	.id_table = led_table,
};

static int __init led_drv_init(void)
{
	led_arr = kzalloc(sizeof(struct stm32mp157_led *) * 6, GFP_KERNEL);
	if(!led_arr)
	{
		printk("kzalloc error\n");
		return -ENOMEM;
	}
	return platform_driver_register(&led_drv);
}

static void __exit led_drv_exit(void)
{
	platform_driver_unregister(&led_drv);
	kfree(led_arr);
}

module_init(led_drv_init);
module_exit(led_drv_exit);
MODULE_LICENSE("GPL");
